package com.zb.log;

import com.alibaba.fastjson.JSON;
import com.zb.annotation.SystemLog;
import com.zb.constant.LogConstant;
import com.zb.constant.OperationLogModuleType;
import com.zb.entity.AccounLoginInfo;
import com.zb.service.ZjSystemLogService;
import com.zb.utils.BrowserUtil;
import com.zb.utils.RequestUtil;
import com.zb.utils.TokenUserUtil;
import com.zb.utils.TokenUtil;
import io.netty.handler.codec.serialization.ObjectDecoder;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Author WEN
 * @Date 2023/7/29 0029
 **/
@Log4j2
@Aspect
@Component
public class SystemLogAspect {
    @Resource
    private ZjSystemLogService systemLogService;

    /**
     * 设置日志的切入点
     */
    @Pointcut("@annotation(com.zb.annotation.SystemLog)")
    public void systemLog() {}

    /**
     * 请求成功返回执行
     * @param joinPoint
     */
    @AfterReturning(value = "systemLog()")
    public void success(JoinPoint joinPoint) {
        try {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            SystemLog systemLog = method.getAnnotation(SystemLog.class);
            HttpServletRequest request = RequestUtil.getRequestByContext();

            systemLogService.saveSuccess(
                    loginInfo(request, systemLog),
                    systemLog.empType(),
                    BrowserUtil.getIpAddress(request),
                    BrowserUtil.getBrowser(request),
                    systemLog.moduleType(),
                    systemLog.type(),
                    request.getRequestURI(),
                    systemLog.remark(),
                    argParam(joinPoint, systemLog)
                    );
        }catch (Exception e) {
            log.error("添加操作日志失败：{}", e);
        }
    }


    /**
     * 出现异常执行
     * @param joinPoint
     * @param throwable
     */
    @AfterThrowing(value = "systemLog()", throwing = "throwable")
    public void error(JoinPoint joinPoint, Throwable throwable) {
        try {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            SystemLog systemLog = method.getAnnotation(SystemLog.class);
            HttpServletRequest request = RequestUtil.getRequestByContext();

            systemLogService.saveError(
                    loginInfo(request, systemLog),
                    systemLog.empType(),
                    BrowserUtil.getIpAddress(request),
                    BrowserUtil.getBrowser(request),
                    systemLog.moduleType(),
                    systemLog.type(),
                    request.getRequestURI(),
                    systemLog.remark(),
                    argParam(joinPoint, systemLog),
                    stackTraceToString(throwable.getClass().getName(), throwable.getMessage(), throwable.getStackTrace())
            );
        }catch (Exception e) {
            log.error("添加异常操作日志失败：{}", e);
        }
    }

    /**
     * 请求参数解析封装
     * @param joinPoint
     * @param systemLog
     * @return
     */
    private String argParam(JoinPoint joinPoint, SystemLog systemLog) {
        if (Objects.equals(OperationLogModuleType.ACCOUNT_MODULE_TYPE, systemLog.moduleType()) || !systemLog.addLogParam()) {
            return "";
        }
        Map<String, Object> paramMap = new HashMap<>();
        // 获取路径后拼接的参数
        HttpServletRequest request = RequestUtil.getRequestByContext();
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (null != parameterMap && parameterMap.size() > 0) {
            Map<String, String> param = new HashMap<>();
            for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                param.put(entry.getKey(), entry.getValue()[0]);
            }
            paramMap.put("parameter", JSON.toJSONString(param));
        }

        String bodyParam = "{}";
        // 获取body的JSON参数
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
                continue;
            }
            bodyParam = JSON.toJSONString(arg);
        }
        paramMap.put("body", bodyParam);
        return JSON.toJSONString(paramMap);
    }

    /**
     * 异常信息解析封装
     * @param exceptionName
     * @param exceptionMessage
     * @param elements
     * @return
     */
    public String stackTraceToString(String exceptionName,String exceptionMessage,StackTraceElement[] elements){
        StringBuilder stringBuilder = new StringBuilder();
        for (StackTraceElement element : elements) {
            stringBuilder.append(element).append("\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + stringBuilder.toString();
        return message;
    }

    /**
     * 获取当前请求账号信息
     * @param request
     * @param systemLog
     * @return
     */
    public AccounLoginInfo loginInfo(HttpServletRequest request, SystemLog systemLog) {
        if (LogConstant.SYSTEM_LOG_EMP_TYPE_2 == systemLog.empType()) {
            return TokenUserUtil.getAccountLoginInfo(request);
        }
        return TokenUtil.getAccountLoginInfo(request);
    }

}
